package com.example.migrator.service.impl;

import com.example.migrator.service.MigrationService;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class MigrationServiceImpl<T> implements MigrationService<T> {

    private static final Logger logger = LoggerFactory.getLogger(MigrationServiceImpl.class);

    @Autowired
    @Qualifier("oracleSqlSessionFactory")
    private SqlSessionFactory oracleSqlSessionFactory;

    @Autowired
    @Qualifier("mysqlSqlSessionFactory")
    private SqlSessionFactory mysqlSqlSessionFactory;

    @Override
    @Transactional(readOnly = true, transactionManager = "oracleTransactionManager")
    public long migrateData(String tableName, int batchSize) {
        return migrateDataWithCondition(tableName, null, batchSize);
    }

    @Override
    @Transactional(readOnly = true, transactionManager = "oracleTransactionManager")
    public long migrateDataWithCondition(String tableName, String whereClause, int batchSize) {
        long totalCount = 0;
        long startTime = System.currentTimeMillis();

        try (SqlSession oracleSession = oracleSqlSessionFactory.openSession();
             SqlSession mysqlSession = mysqlSqlSessionFactory.openSession()) {

            // 获取表的总记录数
            long count = getTableCount(tableName);
            logger.info("开始迁移表 {} 的数据，总记录数: {}", tableName, count);

            // 批量迁移数据
            for (long offset = 0; offset < count; offset += batchSize) {
                Map<String, Object> params = new HashMap<>();
                params.put("tableName", tableName);
                params.put("offset", offset);
                params.put("limit", batchSize);
                if (whereClause != null && !whereClause.isEmpty()) {
                    params.put("whereClause", whereClause);
                }

                // 从Oracle读取数据
                List<Map<String, Object>> dataList = oracleSession.selectList("OracleMapper.selectDataByBatch", params);
                logger.debug("读取批次数据，偏移量: {}, 数量: {}", offset, dataList.size());

                // 写入MySQL数据库
                if (!dataList.isEmpty()) {
                    saveBatchToMySQL(mysqlSession, tableName, dataList);
                    totalCount += dataList.size();
                    mysqlSession.commit();
                }

                // 记录进度
                if ((offset + batchSize) % (batchSize * 10) < batchSize) {
                    double progress = Math.min(100.0, (offset + batchSize) * 100.0 / count);
                    logger.info("迁移进度: {}% ({}/{})", String.format("%.2f", progress), offset + batchSize, count);
                }
            }

            long endTime = System.currentTimeMillis();
            logger.info("表 {} 数据迁移完成，共迁移 {} 条记录，耗时: {} 秒",
                    tableName, totalCount, (endTime - startTime) / 1000.0);

        } catch (Exception e) {
            logger.error("迁移表 {} 的数据时发生错误: {}", tableName, e.getMessage(), e);
            throw new RuntimeException("数据迁移失败", e);
        }

        return totalCount;
    }

    @Override
    public boolean verifyData(String tableName) {
        try (SqlSession oracleSession = oracleSqlSessionFactory.openSession();
             SqlSession mysqlSession = mysqlSqlSessionFactory.openSession()) {

            // 获取Oracle中的记录数
            long oracleCount = getTableCount(tableName);

            // 获取MySQL中的记录数
            Map<String, Object> params = new HashMap<>();
            params.put("tableName", tableName);
            long mysqlCount = mysqlSession.selectOne("MySQLMapper.getTableCount", params);

            logger.info("验证表 {} 的数据一致性: Oracle({}) vs MySQL({})", tableName, oracleCount, mysqlCount);

            return oracleCount == mysqlCount;
        } catch (Exception e) {
            logger.error("验证表 {} 的数据一致性时发生错误: {}", tableName, e.getMessage(), e);
            throw new RuntimeException("数据验证失败", e);
        }
    }

    @Override
    public List<String> getTableMetadata(String tableName) {
        try (SqlSession oracleSession = oracleSqlSessionFactory.openSession()) {
            Map<String, Object> params = new HashMap<>();
            params.put("tableName", tableName);
            return oracleSession.selectList("OracleMapper.getTableMetadata", params);
        } catch (Exception e) {
            logger.error("获取表 {} 的元数据时发生错误: {}", tableName, e.getMessage(), e);
            throw new RuntimeException("获取元数据失败", e);
        }
    }

    @Override
    public long getTableCount(String tableName) {
        try (SqlSession oracleSession = oracleSqlSessionFactory.openSession()) {
            Map<String, Object> params = new HashMap<>();
            params.put("tableName", tableName);
            return oracleSession.selectOne("OracleMapper.getTableCount", params);
        } catch (Exception e) {
            logger.error("获取表 {} 的记录数时发生错误: {}", tableName, e.getMessage(), e);
            throw new RuntimeException("获取记录数失败", e);
        }
    }

    /**
     * 批量保存数据到MySQL数据库
     */
    private void saveBatchToMySQL(SqlSession mysqlSession, String tableName, List<Map<String, Object>> dataList) {
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("tableName", tableName);
            params.put("dataList", dataList);
            
            // 使用批处理插入
            mysqlSession.insert("MySQLMapper.insertBatch", params);
        } catch (Exception e) {
            logger.error("批量保存数据到MySQL时发生错误: {}", e.getMessage(), e);
            mysqlSession.rollback();
            throw new RuntimeException("数据保存失败", e);
        }
    }
}